﻿using LambdaToSql.FrameWork;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;


namespace LambdaToSql
{
    /// <summary>
    /// 解析Lambda表达式
    /// </summary>
    /// <typeparam name="T"></typeparam>
    internal class LambdaRouter1
    {
        /// <summary>
        /// 返回结果
        /// </summary>
        public string Result { get; set; }

        /// <summary>
        /// sql参数集合
        /// </summary>
        public List<SqlParameter> Parameters { get; set; }

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public LambdaRouter1()
        {
            Result = "";
            Parameters = new List<SqlParameter>();
        }

        /// <summary>
        /// 带sql参数的构造函数
        /// </summary>
        /// <param name="parameters"></param>
        public LambdaRouter1(List<SqlParameter> parameters)
        {
            this.Result = "";
            this.Parameters = parameters;
        }


        #region 拆分表达式

        /// <summary>
        /// 拆分表达式
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public string ExpressionRouter(Expression exp)
        {
            var result = "";
            var nodeType = exp.NodeType;
            if (exp is ConstantExpression) //表示具有常数值的表达式
            {
                result = ConstantExpressionProvider(exp);
            }
            else if (exp is UnaryExpression)    //表示具有一元运算符的表达式
            {
                result = UnaryExpressionProvider(exp);
            }
            else if (exp is BinaryExpression)    //表示具有二进制运算符的表达式
            {
                result = BinarExpressionProvider(exp);
            }
            else if (exp is MemberExpression)   //表示访问字段或属性
            {
                result = MemberExpressionProvider(exp);
            }
            else if (exp is NewArrayExpression) //表示创建一个新数组，并可能初始化该新数组的元素
            {
                result = NewArrayExpressionProvider(exp);
            }
            else if (exp is MethodCallExpression)   //表示对静态方法或实例方法的调用
            {
                result = MethodCallExpressionProvider(exp);
            }
            //分割线
            else if (exp is LambdaExpression)   //介绍 lambda 表达式。 它捕获一个类似于 .NET 方法主体的代码块
            {
                result = LambdaExpressionProvider(exp);
            }
            else if (exp is ParameterExpression)    //表示一个命名的参数表达式。
            {
                result = ParameterExpressionProvider(exp);
            }
            else if (exp is NewExpression)    //表示构造函数调用。
            {
                result = NewExpressionProvider(exp);
            }
            this.Result = result;
            return result;
        }
        #endregion

        #region 常量值

        /// <summary>
        /// 常量值
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string ConstantExpressionProvider(Expression exp)
        {
            ConstantExpression ce = (ConstantExpression)exp;
            var name = "@para" + (this.Parameters.Count);
            var value = ce.Value;
            this.Parameters.Add(new SqlParameter(name, value));

            return name;
        }
        #endregion

        #region 一元表达式

        /// <summary>
        /// 一元表达式
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string UnaryExpressionProvider(Expression exp)
        {
            UnaryExpression ue = exp as UnaryExpression;
            var result = ExpressionRouter(ue.Operand);
            return result;
        }
        #endregion

        #region 二元表达式

        /// <summary>
        /// 二元表达式
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string BinarExpressionProvider(Expression exp)
        {
            BinaryExpression be = exp as BinaryExpression;
            Expression left = be.Left;
            Expression right = be.Right;
            ExpressionType type = be.NodeType;
            var str = "";
            str += "(";
            if (be.Right.ToString() == "null")
            {
                str += ExpressionRouter(left);//左边
                if (type == ExpressionType.Equal)
                {
                    str += " IS NULL";//中间 
                }
                else if (type == ExpressionType.NotEqual)
                {
                    str += " IS NOT NULL";//中间 
                }
            }
            else
            {
                str += ExpressionRouter(left);//左边
                str += Tools.TypeCast(type);//中间            
                str += ExpressionRouter(right);//右边
            }
            str += ")";
            return str;
        }
        #endregion

        #region 它捕获一个类似于 .NET 方法主体的代码块

        /// <summary>
        /// 它捕获一个类似于 .NET 方法主体的代码块
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string LambdaExpressionProvider(Expression exp)
        {
            LambdaExpression le = exp as LambdaExpression;
            var result = ExpressionRouter(le.Body);
            return result;
        }
        #endregion

        #region 字段 属性

        /// <summary>
        /// 字段 属性
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string MemberExpressionProvider(Expression exp)
        {
            var result = "";
            if (exp.ToString().StartsWith("value"))
            {
                var obj = Expression.Lambda(exp).Compile().DynamicInvoke();

                var fullName = obj.GetType().FullName;
                if (obj is Array || fullName.StartsWith("System.Collections.Generic"))
                {
                    var list = obj as IEnumerable;
                    var sbTmp = new StringBuilder();
                    var ls = "";
                    foreach (var item in list)
                    {
                        var name = "@para" + (this.Parameters.Count);
                        var value = item;
                        this.Parameters.Add(new SqlParameter(name, value));
                        sbTmp.Append(ls + name);
                        ls = ",";
                    }
                    result = sbTmp.ToString();
                }
                else
                {
                    var name = "@para" + (this.Parameters.Count);
                    this.Parameters.Add(new SqlParameter(name, obj));
                    result = name;
                }
            }
            else
            {
                MemberExpression me = exp as MemberExpression;
                var name = me.Member.Name;
                result = name;
            }
            return result;
        }
        #endregion

        #region 静态方法调用

        /// <summary>
        /// 静态方法调用
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public string MethodCallExpressionProvider(Expression exp)
        {
            var result = "";
            MethodCallExpression mce = exp as MethodCallExpression;

            if (mce.ToString().StartsWith("value"))
            {
                result = MethodCallExpressionValue(mce);
            }
            else
            {
                result = MethodCallExpressionNotValue(mce);
            }
            return result;
        }

        string MethodCallExpressionValue(MethodCallExpression mce)
        {
            var result = "";
            string methodName = mce.Method.Name;

            if (mce.Object != null)
            {
                var par = ExpressionRouter(mce.Object);
                var field = ExpressionRouter(mce.Arguments[0]);
                if (methodName == "Contains")
                {
                    if (!string.IsNullOrEmpty(par))
                    {
                        result = string.Format("{0} in ({1})", field, par);
                    }
                    else
                    {
                        result = "1=0";
                    }
                }
            }
            else
            {
                var par = ExpressionRouter(mce.Arguments[0]);
                var field = ExpressionRouter(mce.Arguments[1]);
                if (methodName == "NotContains" && par != "")
                {
                    result = string.Format("{0} not in ({1})", field, par);
                }
                else
                {
                    result = "1=1";
                }
            }
            return result;
        }
        string MethodCallExpressionNotValue(MethodCallExpression mce)
        {
            var result = "";
            string methodName = mce.Method.Name;

            if (mce.Object != null)
            {
                var field = ExpressionRouter(mce.Object);
                if (mce.Arguments.Count() == 0)
                {
                    if (methodName == "ToLower")
                    {
                        result = string.Format("Lower({0})", field);
                    }
                }
                else if (mce.Arguments.Count() == 1)
                {
                    var par = ExpressionRouter(mce.Arguments[0]);
                    if (methodName == "Contains")
                    {
                        if (mce.Object.NodeType == ExpressionType.MemberAccess)
                        {
                            result = string.Format("{0} like {1}", field, par);
                            SetParameters(par, "%{0}%");
                        }
                    }
                    else if (methodName == "StartsWith")
                    {
                        if (mce.Object.NodeType == ExpressionType.MemberAccess)
                        {
                            result = string.Format("{0} like {1}", field, par);
                            SetParameters(par, "{0}%");
                        }
                    }
                    else if (methodName == "EndsWith")
                    {
                        if (mce.Object.NodeType == ExpressionType.MemberAccess)
                        {
                            result = string.Format("{0} like {1}", field, par);
                            SetParameters(par, "%{0}");
                        }
                    }
                }
            }
            else
            {
                if (methodName == "NotContains")
                {
                    var field = ExpressionRouter(mce.Arguments[0]);
                    var par = ExpressionRouter(mce.Arguments[1]);
                    result = string.Format("{0} not like {1}", field, par);
                    SetParameters(par, "%{0}%");
                }
                else if (methodName == "DateAdd")
                {
                    var field = ExpressionRouter(mce.Arguments[0]);
                    var par = ExpressionRouter(mce.Arguments[1]);

                    //result = string.Format("dateadd(dd, {1}, {0}) ", field, par); 

                    var value = Expression.Lambda(mce.Arguments[1]).Compile().DynamicInvoke().ToString();
                    result = string.Format("dateadd(dd, {1}, {0}) ", field, value).Replace("\"", "");

                    SetParameters(par, "{0}");
                }
                else if (methodName == "ConvertDate")
                {
                    var field = ExpressionRouter(mce.Arguments[0]);
                    var par = ExpressionRouter(mce.Arguments[1]);

                    result = string.Format("convert(varchar(256), {1}, {0})", field, mce.Arguments[1]).Replace("\"", "");
                    SetParameters(par, "{0}");
                }
                else
                {
                    var name = "@para" + Parameters.Count;
                    var value = Expression.Lambda(mce).Compile().DynamicInvoke().ToString();
                    this.Parameters.Add(new SqlParameter(name, value));
                    result = name;
                }
            }
            return result;
        }

        #endregion

        #region 数组

        /// <summary>
        /// 表示创建一个新数组，并可能初始化该新数组的元素
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string NewArrayExpressionProvider(Expression exp)
        {
            NewArrayExpression ae = exp as NewArrayExpression;
            var sb = new StringBuilder();
            var ls = "";
            foreach (Expression ex in ae.Expressions)
            {
                sb.Append(ls + ExpressionRouter(ex));
                ls = ",";
            }
            return sb.ToString();
        }
        #endregion

        #region 表示一个命名的参数表达式

        /// <summary>
        /// 表示一个命名的参数表达式
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        private string ParameterExpressionProvider(Expression exp)
        {
            ParameterExpression pe = exp as ParameterExpression;
            return pe.Type.Name;
        }
        #endregion

        #region 表示构造函数调用

        /// <summary>
        /// 表示构造函数调用
        /// </summary>
        /// <param name="expression"></param>
        /// <returns></returns>
        private string NewExpressionProvider(Expression expression)
        {
            var sb = new StringBuilder();
            var ls = "";
            var newExpression = expression as NewExpression;//转换为匿名对象表达式
            foreach (var item in newExpression.Arguments)//遍历所有参数
            {
                var memberExpression = item as MemberExpression;//转换为属性访问表达式
                var name = memberExpression.Member.Name;//返回属性名
                sb.Append(ls + name);
                ls = ",";
            }
            return sb.ToString();
        }
        #endregion

        //************************************************

        #region SetParameters

        /// <summary>
        /// 重新设置SetParameters值
        /// </summary>
        /// <param name="parameterName">名称</param>
        /// <param name="format">格式 例如 %{0}%,{0}%</param>
        public void SetParameters(string parameterName, string format)
        {
            foreach (var item in this.Parameters)
            {
                if (item.ParameterName == parameterName)
                {
                    item.Value = string.Format(format, item.Value);
                    break;
                }
            }
        }
        #endregion

    }
}
